﻿/* MVT Charts for Silverlight - Version 1
 * 2008 by Mario Meir-Huber
 * mailto: i-mameir@microsoft.com
 *         mario@meirhuber.de
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Globalization;
using System.Diagnostics;
using ChartLibrary.Base;

/*
 * ChartLibrary.Charts is the Namespace for all charts.
 */
namespace ChartLibrary.Charts
{
    /// <summary>
    /// displays more bars in a row
    /// </summary>
    public partial class MultiBarChart : AxisCharts
    {
        public event ItemHandler ItemMouseEnter;  //occurs when the mouse enters the item
        public event ItemHandler ItemMouseLeave;  //occurs when the mouse leaves the item
        public event ItemHandler ItemClicked;     //occurs when a mouse click is performed on the item

        /// <summary>
        /// this displays more columns for a bar chart
        /// </summary>
        public MultiBarChart()
        {
            LayoutRoot.SizeChanged += new SizeChangedEventHandler(LayoutRoot_SizeChanged);
            this.DrawAreaChanged += new StateChangedEvent(MultiBarChart_DrawAreaChanged);
        }

        /// <summary>
        /// updates the chart if there were layout updates
        /// </summary>
        void MultiBarChart_DrawAreaChanged()
        {
            UpdateChart();
        }

        /// <summary>
        /// updates the chart if the size has changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void LayoutRoot_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (e.NewSize.Height > 0 && e.NewSize.Width > 0) UpdateChart();
        }

        /// <summary>
        /// updates the chart
        /// </summary>
        private void UpdateChart()
        {
            double singleHeight = 0.0;
            double singleWidth = 0.0;
            double spacing = 10.0;
            double left = 0.0;

            int maxitem = 0;
            int count = -1;

            Rectangle rtTemp = null;
            Storyboard stTemp = null;

            TransformGroup tmpTransform = null;

            //calculates the highes point
            foreach (Data.DataSource ds1 in Data)
            {
                if (ds1.Length > maxitem) maxitem = ds1.Length;
            }

            //iterates over all data sources
            foreach (Data.DataSource ds in Data)
            {
                singleHeight = Area.Height / Data.Max;
                singleWidth = (Area.Width - (spacing) * maxitem - Area.StartX) / (maxitem * Data.Count);

                if (singleWidth < 0) throw new NotSupportedException("There are too much items in the list, which cannnot be displayed");

                count++;

                for (int i = 0; i < ds.Length; i++)
                {
                    rtTemp = LayoutRoot.FindName("rt_" + count.ToString() + "_" + i.ToString()) as Rectangle;

                    if (null == rtTemp)
                    {
                        rtTemp = new Rectangle();
                        rtTemp.SetValue(Canvas.NameProperty, "rt_" + count.ToString() + "_" + i.ToString());
                        LayoutRoot.Children.Add(rtTemp);

                        rtTemp.MouseLeftButtonUp += new MouseButtonEventHandler(OnItemClick);
                        rtTemp.MouseEnter += new MouseEventHandler(OnItemEnter);
                        rtTemp.MouseLeave += new MouseEventHandler(OnItemLeave);
                    }

                    left = spacing * (i + 1) + i * singleWidth * Data.Count + singleWidth * count + Area.StartX;


                    rtTemp.SetValue(Canvas.LeftProperty, left);

                    rtTemp.Width = singleWidth;
                    rtTemp.Height = singleHeight * ds[i].Value;
                    rtTemp.Fill = ds.Fill;

                    tmpTransform = new TransformGroup();
                    tmpTransform.Children.Add(new ScaleTransform());
                    tmpTransform.Children.Add(new TranslateTransform());

                    rtTemp.RenderTransform = tmpTransform;

                    switch (ItemType)
                    {
                        case ChartLibrary.Base.ItemType.BarRadiusXY:
                            {
                                rtTemp.RadiusX = 10.0;
                                rtTemp.RadiusY = 10.0;
                                break;
                            }
                        case ChartLibrary.Base.ItemType.BarRounded:
                            {
                                LinearGradientBrush lgr = new LinearGradientBrush();
                                lgr.StartPoint = new Point(0.0, 0.5);
                                lgr.EndPoint = new Point(1.0, 0.5);

                                lgr.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(40, 255, 255, 255), Offset = 0.0 });
                                lgr.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(255, 255, 255, 255), Offset = 0.20 });
                                lgr.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(255, 255, 255, 255), Offset = 0.8 });
                                lgr.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(40, 255, 255, 255), Offset = 1 });

                                rtTemp.OpacityMask = lgr;
                                break;
                            }
                        default:
                            {
                                rtTemp.RadiusX = 0.0;
                                rtTemp.RadiusY = 0.0;
                                rtTemp.OpacityMask = null;
                                break;
                            }
                    }

                    rtTemp.SetValue(Canvas.TopProperty, Area.Height - rtTemp.Height + Area.StartY);

                    if (Animation == ChartLibrary.Base.Animation.FxOpacity || Animation == ChartLibrary.Base.Animation.FxOpacityAsynchrounous)
                    {
                        rtTemp.Opacity = 0.0;
                    }

                    stTemp = LayoutRoot.FindName("_story" + count.ToString() + "_" + i.ToString()) as Storyboard;

                    if (null == stTemp)
                    {
                        stTemp = AddAnimation((string)rtTemp.GetValue(Canvas.NameProperty), i, rtTemp.Height);

                        try
                        {
                            stTemp.SetValue(Canvas.NameProperty, "_story" + count.ToString() + "_" + i.ToString());
                            //LayoutRoot.Resources.Add(stTemp);

                            stTemp.Completed += new EventHandler(stTemp_Completed);
                            stTemp.Begin();
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.Message);
                        }
                    }
                    else
                    {
                        stTemp.Begin();
                    }
                }
            }
        }

        /// <summary>
        /// Occurs when the mouse enters an item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnItemLeave(object sender, MouseEventArgs e)
        {
            if (null != ItemMouseLeave) ItemMouseLeave(sender, new ItemEventArgs());
        }

        /// <summary>
        /// Occurs when an item was clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnItemClick(object sender, MouseButtonEventArgs e)
        {
            if (null != ItemClicked)
            {
                ItemClicked(sender, new ItemEventArgs());
            }
        }

        /// <summary>
        /// Occurs when the mouse enters an item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnItemEnter(object sender, MouseEventArgs e)
        {
            if (null != ItemMouseEnter) ItemMouseEnter(sender, new ItemEventArgs());
        }

        /// <summary>
        /// removes a storyboard if it has completed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void stTemp_Completed(object sender, EventArgs e)
        {
            Storyboard st = (Storyboard)sender;
            //LayoutRoot.Resources.Remove(st);
        }

        /// <summary>
        /// adds a new animation to an item
        /// </summary>
        /// <param name="target">target to be animated</param>
        /// <param name="AsynchId">information on an asynch id</param>
        /// <param name="height">endposition to be used</param>
        /// <returns></returns>
        private Storyboard AddAnimation(string target, int AsynchId, double height)
        {
            string anim = "";
            Storyboard retval = null;

            height = Math.Round(height, 2);

            switch (Animation)
            {
                default:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\"/>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxDropDown:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<PointAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransformOrigin)\"" +
                        " BeginTime=\"00:00:00\"><SplinePointKeyFrame KeyTime=\"00:00:00\" Value=\"0.5,0.0\"/></PointAnimationUsingKeyFrames>" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)\" " +
                        "BeginTime=\"00:00:00\">" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00\" Value=\"0.0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.4).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.1\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.5).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.0\"/>" +
                        "</DoubleAnimationUsingKeyFrames>" +

                        "</Storyboard>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxDropDownAsynchronous:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<PointAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransformOrigin)\"" +
                        " BeginTime=\"00:00:00\"><SplinePointKeyFrame KeyTime=\"00:00:00\" Value=\"0.5,0.0\"/></PointAnimationUsingKeyFrames>" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)\" " +
                        "BeginTime=\"00:00:00\">" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00\" Value=\"0.0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.1 * AsynchId).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.1\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.1 * AsynchId + 0.3).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.0\"/>" +
                        "</DoubleAnimationUsingKeyFrames>" +

                        "</Storyboard>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxRiseUp:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<PointAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransformOrigin)\"" +
                        " BeginTime=\"00:00:00\"><SplinePointKeyFrame KeyTime=\"00:00:00\" Value=\"0.5,1.0\"/></PointAnimationUsingKeyFrames>" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)\" " +
                        "BeginTime=\"00:00:00\">" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00\" Value=\"0.0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.4).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.1\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.5).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.0\"/>" +
                        "</DoubleAnimationUsingKeyFrames>" +

                        "</Storyboard>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxRiseUpAsynchronous:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<PointAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransformOrigin)\"" +
                        " BeginTime=\"00:00:00\"><SplinePointKeyFrame KeyTime=\"00:00:00\" Value=\"0.5,1.0\"/></PointAnimationUsingKeyFrames>" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)\" " +
                        "BeginTime=\"00:00:00\">" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00\" Value=\"0.0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.1 * AsynchId).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.1\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:" + (0.1 * AsynchId + 0.3).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"1.0\"/>" +
                        "</DoubleAnimationUsingKeyFrames>" +
                        "</Storyboard>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxOpacity:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.Opacity)\" " +
                        "BeginTime=\"00:00:00\"><SplineDoubleKeyFrame KeyTime=\"00:00:00\" Value=\"0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:00.5000000\" Value=\"1\"/></DoubleAnimationUsingKeyFrames></Storyboard>";
                        break;
                    }
                case ChartLibrary.Base.Animation.FxOpacityAsynchrounous:
                    {
                        anim = "<Storyboard xmlns=\"http://schemas.microsoft.com/client/2007\">" +
                        "<DoubleAnimationUsingKeyFrames Storyboard.TargetName=\"" + target + "\" Storyboard.TargetProperty=\"(UIElement.Opacity)\" " +
                        "BeginTime=\"00:00:00\"><SplineDoubleKeyFrame KeyTime=\"00:00:0" + (0.1 * AsynchId).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + "\" Value=\"0\"/>" +
                        "<SplineDoubleKeyFrame KeyTime=\"00:00:0" + (0.1 * AsynchId + 0.3).ToString(new NumberFormatInfo() { CurrencyDecimalSeparator = "." }) + " \" Value=\"1\"/></DoubleAnimationUsingKeyFrames></Storyboard>";
                        break;
                    }
            }

            retval = (Storyboard)System.Windows.Markup.XamlReader.Load(anim);

            return retval;
        }
    }
}
